﻿#region COPYRIGHT© 2009-2013 Phillip Clark. All rights reserved.

// For licensing information see License.txt (MIT style licensing).

#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using FlitBit.Parse;

namespace FlitBit.Json
{
	public enum JsonNodeKind
	{
		None = 0,
		Object = 1,
		Array = 2,
		StringLiteral = 3,
		NumericLiteral = 4,
		BooleanLiteral = 5,
		Null = 6,
		Member = 7,
	}

	/// <summary>
	/// Base class for nodes parsed from JSON.
	/// </summary>
	public abstract class JsonNode
	{
		static readonly int CHashCodeSeed = typeof(JsonNode).AssemblyQualifiedName.GetHashCode();

		/// <summary>
		/// Creates a new instance.
		/// </summary>
		/// <param name="kind">the new instance's kind</param>
		protected JsonNode(JsonNodeKind kind)
		{
			this.Kind = kind;
		}

		/// <summary>
		/// Gets the instance's kind.
		/// </summary>
		public JsonNodeKind Kind { get; private set; }

		/// <summary>
		/// Determines if the instance is equal to another.
		/// </summary>
		/// <param name="obj">the other</param>
		/// <returns><em>true</em> if equal; otherwise <em>false</em></returns>
		public override bool Equals(object obj)
		{
			return (obj is JsonNode) && Equals((JsonNode) obj);
		}

		/// <summary>
		/// Calculates the instance's hashcode.
		/// </summary>
		/// <returns>
		/// The instance's hashcode.
		/// </returns>
		public override int GetHashCode()
		{
			const int prime = 999067;
			var code = CHashCodeSeed * prime;
			code ^= (int) Kind * prime;
			return code;
		}

		/// <summary>
		/// Gets the node's string representation.
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			var formatter = new JsonFormatter();
			this.WriteFormatted(formatter);
			return formatter.ToString();
		}

		/// <summary>
		/// Determines if the instance is equal to another.
		/// </summary>
		/// <param name="other">the other</param>
		/// <returns><em>true</em> if equal; otherwise <em>false</em></returns>
		public bool Equals(JsonNode other)
		{
			return (other != null)
				&& other.Kind == this.Kind;
		}

		/// <summary>
		/// Writes the node's JSON representation to the formatter.
		/// </summary>
		/// <param name="formatter">the formatter</param>
		protected internal virtual void WriteFormatted(JsonFormatter formatter)
		{
			Contract.Requires<ArgumentNullException>(formatter != null, "formatter cannot be null");

			formatter.Write(this.ToString());
		}

		/// <summary>
		/// Filters tokens for errors.
		/// </summary>
		/// <param name="tokens"></param>
		/// <returns></returns>
		protected static IEnumerable<Token<JsonTokenKind>> Validate(IEnumerable<Token<JsonTokenKind>> tokens)
		{
			return tokens as Token<JsonTokenKind>[] ?? tokens.Where(t => t.IsError);
		}
	}
}